/**
 * WebRiders Framework - Data-related stuff:
 *     set, array, object, other data-structures and related methods
 * 
 * @version 0.9
 * @author Bryzgunov Rostislav (aka kottenator)
 * @license MIT or GPL
 * 
 * Dependencies:
 *     webriders.core-0.9.js
 */

 WR.data = {}

/**
 * Collection
 * Something between Array and Object :)
 * @param {Array} items
 * @constructor
 */
WR.data.Collection = function(items) {
    this.items = [];

    if (items instanceof WR.data.Collection)
        items = items.toArray();

    if ($.isArray(items))
        this.proxyFn('push', items);
}

WR.data.Collection.prototype = {
    // @private
    items: null,

    getLength: function() {
        return this.items.length;
    },

    proxyFn: function(fnName, args) {
        return this.items[fnName].apply(this.items, args);
    },

    /**
     * Proxy for [].push(new1, [new2, new3, ...])
     * Adds one ore more elements to the end of the array
     * @return {Number} new array length
     */
    push: function() {
        return this.proxyFn('push', arguments);
    },

    /**
     * Proxy for [].pop()
     * Removes the last element of the array
     * @return removed element
     */
    pop: function() {
        return this.proxyFn('pop', arguments);
    },

    /**
     * Proxy for [].shift()
     * Removes the first element of the array
     * @return removed element
     */
    shift: function() {
        return this.proxyFn('shift', arguments);
    },

    /**
     * Proxy for [].unshift(new1, [new2, new3, ...])
     * Adds one ore more elements to the beginning of the array
     * @return {Number} new array length
     */
    unshift: function() {
        return this.proxyFn('unshift', arguments);
    },

    /**
     * Proxy for [].slice(fromPos, [toPos])
     * @return {WebRiders.data.Collection} sliced sub-collection (from fromPos index till toPos index)
     */
    slice: function() {
        this.proxyFn('slice', arguments);
        return this;
    },

    /**
     * Proxy for [].splice(fromPos, [howMany, [new1, [new2, new3, ...]]])
     * Removes howMany elements from fromPos index and inserts there one or more new elements
     * @return {WebRiders.data.Collection} spliced sub-collection
     */
    splice: function() {
        this.proxyFn('splice', arguments);
        return this;
    },

    /**
     * Get item by id
     * @param id
     * @return found element or undefined
     */
    getById: function(id) {
        return this.get({ 'id': id })[0];
    },

    /**
     * Get item by something
     *
     * @param {String/Number/Function/Object} by:
     *     0, 1, 2, 'asd', 'qwe' - returns array item by index (or smth)
     *     { year: 2009, sum: 15 } - returns array items, that has EXACTLY the same params (year === 2009, sum === 15)
     *     { x: null, y: undefined } - true, when item.x === null and item.x in not defined
     *     { z: '*' } - spacial case. Equal to any (you can't use '*' as just '*', sorry).
     *     function(o) { return o.year === 2009 && sum === 15; } - filter by function
     *
     * @return {Array} found elements (may be empty but still Array)
     */
    get: function(by) {
        // Filter by params set
        if (typeof by == "object") {
            var ret = [];

            function _cmp(o1, o2) {
                for (var k in o1) {
                    if (o1.hasOwnProperty(k)) {
                        if (o1[k] === '*' && o2.hasOwnProperty(k))
                            continue;
                        
                        if (o1[k] !== o2[k])
                            return false;
                    }

                }
                return true;
            }

            for (var i = 0, it = this.items, l = it.length; i < l; i++)
                if (_cmp(by, it[i]))
                    ret.push(it[i]);

            return ret;
        }
        // Filter by function
        else if (typeof by == "function") {
            var ret = [];

            for (var i = 0, it = this.items, l = it.length; i < l; i++)
                if (by.call(it[i], it[i]))
                    ret.push(it[i]);

            return ret;
        }
        // Get by index
        else {
            return this.items[by];
        }
    },

    /**
     * Items iterator
     * @param {Function/String} fn - function or method name
     */
    each: function(fn) {
        if (typeof fn == "function")
            return $.each(this.items, fn);
        else if (typeof fn == "string")
            return $.each(this.items, function() {
                this[fn]();
            });
    },

    toArray: function() {
        return this.items;
    },

    toString: function() {
        return this.items.toString();
    }
}

/**
 * Alias for WR.data.Collection
 */
WR.data.C = WR.data.Collection;